#!/bin/sh

#
# Follow those steps to install and start crc in Ubuntu 22.04:
# 
#     sudo wget https://developers.redhat.com/content-gateway/file/pub/openshift-v4/clients/crc/2.51.0/crc-linux-amd64.tar.xz -O - | sudo tar xf --strip-components=1 -C /usr/local/bin/ -f - crc
#     sudo chmod a+x /usr/local/bin/crc
#     sudo apt install qemu-kvm libvirt-daemon libvirt-daemon-system network-manager
#     # apply patch for apparmor configuration for libvirt from https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/1962035/comments/2
#     echo '/usr/share/OVMF/** rk,' | sudo tee -a /etc/apparmor.d/abstractions/libvirt-qemu
#     echo '/**/nvram/*_VARS.fd rwk,' | sudo tee -a /etc/apparmor.d/abstractions/libvirt-qemu
#     sudo systemctl reload apparmor.service
#     sudo systemctl restart libvirtd
#     crc config set skip-check-daemon-systemd-unit true
#     crc config set skip-check-daemon-systemd-sockets true
#     crc config set network-mode user
#     crc config set host-network-access true
#     crc config set nameserver 8.8.8.8
#     crc config set enable-cluster-monitoring true
#     crc setup
#     mkdir -p "$HOME/.crc"
#
# Download CRC pull secret from https://console.redhat.com/openshift/create/local to "$HOME/.crc/pull-secret"
#
CRC_EXPANDABLE_STORAGE_CLASSNAME="${EXPANDABLE_STORAGE_CLASSNAME:-expandable-sc}"
CRC_ENABLE_DAEMON="${CRC_ENABLE_DAEMON:-true}"

if [ "$K8S_VERSION" = "$DEFAULT_K8S_VERSION" ]
then
  >&2 echo "Warning: using kubernetes version 1.22.3 since e2e default $DEFAULT_K8S_VERSION is not available for crc"
  K8S_VERSION=1.22.3
fi
export K8S_CRC_CPUS="${K8S_CRC_CPUS:-4}"
export K8S_CRC_MEMORY="${K8S_CRC_MEMORY:-9216}"
export K8S_CRC_DISK="${K8S_CRC_DISK:-60}"

get_k8s_env_version() {
  echo "crc version $(crc version | head -n 1 | cut -d ' ' -f 4)"
  echo
}

update_crc_config() {
  local CRC_LOGIN_COMMAND
  CRC_LOGIN_COMMAND="$(crc console --credentials)"
  CRC_LOGIN_COMMAND="$(printf '%s' "$CRC_LOGIN_COMMAND" | cut -d "'" -f 2 | grep kubeadmin | sed 's#oc #~/.crc/bin/oc/oc #')"
  if ! timeout -s KILL 10s $CRC_LOGIN_COMMAND > /dev/null 2>&1
  then
    if crc status | grep '^OpenShift:[[:space:]]\+Stopped[[:space:]]'
    then
      return 1
    fi
    wait_until -t 60 eval "crc status | grep '^OpenShift:[[:space:]]\+Running[[:space:]]'"
    wait_until -t 60 eval "timeout -s KILL 10s $CRC_LOGIN_COMMAND > /dev/null 2>&1"
  fi

  ~/.crc/bin/oc/oc adm policy add-scc-to-user hostmount-anyuid -n default -z default
  kubectl patch networks.config.openshift.io cluster --type json \
    -p '[{"op":"replace","path":"/spec/externalIP/policy","value":{"allowedCIDRs":["0.0.0.0/0"]}}]'
  kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.22/deploy/local-path-storage.yaml
  ~/.crc/bin/oc/oc adm policy add-scc-to-user hostmount-anyuid -n local-path-storage -z local-path-provisioner-service-account
  kubectl get storageclass -o name | xargs -I % kubectl annotate % --overwrite storageclass.kubernetes.io/is-default-class=false
  kubectl annotate storageclass local-path --overwrite storageclass.kubernetes.io/is-default-class=true
}

update_k8s_config() {
  mkdir -p "$HOME/.kube"
  if [ "$K8S_FROM_DIND" = true ]
  then
    echo "Can not use crc environment from docker"
    exit 1
  else
    ~/.crc/bin/oc/oc config view --raw > "$HOME/.kube/config-oc"
  fi

  (
  export KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}"
  if [ -s "$KUBECONFIG" ]
  then
    KUBECONFIG="$HOME/.kube/config-oc":"$KUBECONFIG" \
      kubectl config view --raw > "$HOME/.kube/config-merged"
    mv "$HOME/.kube/config-merged" "$KUBECONFIG"
  else
    mv "$HOME/.kube/config-oc" "$KUBECONFIG"
  fi
  chmod 700 "$KUBECONFIG"
  )
}

reuse_k8s() {
  local RESULT
  local EXIT_CODE
  try_function update_crc_config

  if ! "$RESULT" > /dev/null 2>&1
  then
    reset_k8s true
  fi

  echo "Reusing crc environment"
  update_crc_config
  update_k8s_config
  crc_start_debug_node
  crc_run_on_node setenforce 0
}

reset_k8s() {
  if [ "$K8S_SKIP_RESET" = "true" ]
  then
    exit 1
  fi

  if [ "$K8S_VERSION" != 1.22.3 ]
  then
    echo "Only kubernetes version 1.22.3 is available for crc environment"
    return 1
  fi

  echo "Setting up crc environment..."

  if [ "$1" != true ]
  then
    crc delete -f || true
    virsh destroy crc || true
    virsh undefine crc || true
  else
    crc stop -f || true
  fi
  if [ "$CRC_ENABLE_DAEMON" = true ]
  then
    screen -ls 2>/dev/null | grep '\.crc-daemon' \
      | tr '[:space:]' ' ' | cut -d ' ' -f 2 | cut -d . -f 1 \
      | xargs -r -I % kill %
  fi

  rm -Rf "$HOME/.crc/machines"

  if ! [ -f "$HOME/.crc/pull-secret" ]
  then
    echo "Copy pull secret from https://console.redhat.com/openshift/create/local to $HOME/.crc/pull-secret"
    return 1
  fi

  if [ "$CRC_ENABLE_DAEMON" = true ]
  then
    screen -dmS crc-daemon -L -Logfile "$HOME/.crc/crc-daemon.log" crc daemon
  fi
  export PATH="$PATH:/usr/sbin"
  crc start \
    $([ -z "$E2E_CRC_BUNDLE" ] || printf %s "--bundle $E2E_CRC_BUNDLE") \
    --pull-secret-file "$HOME/.crc/pull-secret" \
    --cpus "$K8S_CRC_CPUS" \
    --memory "$K8S_CRC_MEMORY" \
    --disk-size "$K8S_CRC_DISK"

  update_crc_config
  update_k8s_config
  crc_start_debug_node
  crc_run_on_node setenforce 0

  echo "...done"
}

delete_k8s() {
  echo "Deleting crc environment..."

  crc delete -f || true
  if [ "$CRC_ENABLE_DAEMON" = true ]
  then
    screen -ls 2>/dev/null | grep '\.crc-daemon' \
      | tr '[:space:]' ' ' | cut -d ' ' -f 2 | cut -d . -f 1 \
      | xargs -r -I % kill %
  fi

  echo "...done"
}

load_image_k8s() {
  local IMAGE_ID
  IMAGE_ID="$( (docker inspect --format '{{ .ID }}' "$1" 2>/dev/null || printf unknown) | grep -v '^$')"
  local CRC_IMAGE_ID
  CRC_IMAGE_ID="$( (crc_run_on_node crictl inspecti -o json "$1" 2>/dev/null || printf '{"status": {"id": "unknown"}}') | jq -r '"sha256:" + .status.id' | grep -v '^$')"
  if [ "$IMAGE_ID" = unknown ] && [ "$KIND_IMAGE_ID" != unknown ]
  then
    echo "Image $1 already loaded in crc environemnt"
    return
  fi
  if [ "$CRC_IMAGE_ID" = "$IMAGE_ID" ]
  then
    echo "Image $1 already loaded in crc environemnt"
    return
  fi

  echo "Loading images in crc environemnt is not supported"
  return 1
  # echo "Loading image $1 in crc environemnt"
  # docker save "$1" | crc_run_on_node podman load
  # if crc_run_on_node podman inspect "localhost/$1" > /dev/null 2>&1
  # then
  #   crc_run_on_node podman tag "localhost/$1" "docker.io/$1"
  # fi
}

pull_image_k8s() {
  local AUTH
  AUTH="$(jq -r '.auths|to_entries|.[]|.key + "|" + .value.auth' "${HOME}/.docker/config.json" \
    | grep -F "${1%%/*}" | head -n 1 | cut -d '|' -f 2)"
  if [ -n "$AUTH" ]
  then
    crc_run_on_node crictl pull --auth "$AUTH" "$1"
  else
    crc_run_on_node crictl pull "$1"
  fi

  echo "Pulled image $1 in crc environemnt"
}

tag_image_k8s() {
  crc_run_on_node podman tag "$1" "$2"

  echo "Tagged image $1 as $2 in crc environemnt"
}

crc_start_debug_node() {
  screen -ls 2>/dev/null | grep '\.crc-debug' \
    | tr '[:space:]' ' ' | cut -d ' ' -f 2 | cut -d . -f 1 \
    | xargs -r -I % kill %

  local NODE
  NODE="$(kubectl get node -o name)"
  NODE="$(printf '%s' "$NODE" | head -n 1)"
  screen -dmS crc-debug -L -Logfile "$HOME/.crc/crc-debug.log" \
    ~/.crc/bin/oc/oc debug -n default -q "$NODE" -- sh -c 'while true; do sleep 300; done'

  wait_until crc_run_on_node true
}

crc_run_on_node() {
  local NODE POD PID
  NODE="$(kubectl get node -o name)"
  NODE="$(printf '%s' "$NODE" | head -n 1)"

  POD="$(kubectl get pod -o name | cut -d / -f 2 | grep "^${NODE#node/}-debug" | head -n 1)"
  PID="$(kubectl exec "$POD" -- sh -c \
    'for FILE in /proc/[0-9]*; do cat "$FILE/cmdline" 2>/dev/null | tr "\0" " " | grep -q "^\([^ ]*/\)\?kubelet " && echo "${FILE##*/}" && exit; done')"
  kubectl exec -i "$POD" -- \
    nsenter --target "$PID" --mount --uts --ipc --net --pid "$@"
}

excluded_namespaces() {
  echo "default"
  echo "kube-.*"
  echo "openshift"
  echo "openshift-.*"
  echo "local-path-storage"
}

excluded_validatingwebhookconfigurations() {
  echo "machine-api"
  echo ".*\.openshift\.io"
}

excluded_mutatingwebhookconfigurations() {
  echo "machine-api"
  echo ".*\.openshift\.io"
}

excluded_customresourcedefinitions() {
  echo ".*\.openshift\.io"
  echo ".*\.metal3\.io"
  echo ".*\.operators\.coreos\.com"
  echo ".*\.cni\.cncf\.io"
  echo ".*\.monitoring\.coreos\.com"
  echo ".*\.k8s\.io"
  echo ".*\.x-k8s\.io"
  echo ".*\.k8s\.ovn\.org"
}

excluded_clusterroles() {
  echo "admin"
  echo "aggregate-olm-edit"
  echo "aggregate-olm-view"
  echo "basic-user"
  echo "cloud-credential-operator-role"
  echo "cluster-admin"
  echo "cluster-autoscaler"
  echo "cluster-autoscaler-operator"
  echo "cluster-autoscaler-operator:cluster-reader"
  echo "cluster-baremetal-operator"
  echo "cluster-debugger"
  echo "cluster-image-registry-operator"
  echo "cluster-monitoring-operator"
  echo "cluster-node-tuning-operator"
  echo "cluster-node-tuning:tuned"
  echo "cluster-reader"
  echo "cluster-samples-operator"
  echo "cluster-samples-operator-proxy-reader"
  echo "cluster-status"
  echo "console"
  echo "console-extensions-reader"
  echo "console-operator"
  echo "dns-monitoring"
  echo "edit"
  echo "global-operators-admin"
  echo "global-operators-edit"
  echo "global-operators-view"
  echo "helm-chartrepos-viewer"
  echo "insights-operator"
  echo "insights-operator-gather"
  echo "kube-apiserver"
  echo "machine-api-controllers"
  echo "machine-api-operator"
  echo "machine-api-operator:cluster-reader"
  echo "machine-config-controller"
  echo "machine-config-controller-events"
  echo "machine-config-daemon"
  echo "machine-config-daemon-events"
  echo "machine-config-server"
  echo "marketplace-operator"
  echo "metrics-daemon-role"
  echo "multus"
  echo "multus-admission-controller-webhook"
  echo "network-diagnostics"
  echo "olm-operators-admin"
  echo "olm-operators-edit"
  echo "olm-operators-view"
  echo "openshift-.*"
  echo "operatorhub-config-reader"
  echo "packagemanifests-v1-admin"
  echo "packagemanifests-v1-edit"
  echo "packagemanifests-v1-view"
  echo "prometheus-k8s-scheduler-resources"
  echo "registry-admin"
  echo "registry-editor"
  echo "registry-monitoring"
  echo "registry-viewer"
  echo "router-monitoring"
  echo "self-access-reviewer"
  echo "self-provisioner"
  echo "storage-admin"
  echo "sudoer"
  echo "system:.*"
  echo "view"
  echo "whereabouts-cni"
  echo "cloud-node-manager"
  echo "cluster-samples-operator-imageconfig-reader"
  echo "cloud-controller-manager"
  echo "machine-api-operator-ext-remediation"
  echo "local-path-provisioner-role"
  echo "multus-.*"
  echo "cluster-monitoring-operator-namespaced"
  echo "control-plane-machine-set-operator"
  echo "csi-snapshot-controller-operator-clusterrole"
  echo "crc-hostpath-external-provisioner-runner"
  echo "machine-api-controllers-metal3-remediation"
  echo "machine-api-controllers-metal3-remediation-aggregation"
  echo "machine-os-builder-events"
  echo "machine-os-builder"
  echo "net-attach-def-project"
  echo "network-node-identity"
  echo "olm.og.global-operators.admin-.*"
  echo "olm.og.global-operators.edit-.*"
  echo "olm.og.global-operators.view-.*"
  echo "olm.og.olm-operators.admin-.*"
  echo "olm.og.olm-operators.edit-.*"
  echo "olm.og.olm-operators.view-.*"
  echo "olm.og.openshift-cluster-monitoring.admin-.*"
  echo "olm.og.openshift-cluster-monitoring.edit-.*"
  echo "olm.og.openshift-cluster-monitoring.view-.*"
  echo "project-helm-chartrepository-editor"
}

excluded_clusterrolebindings() {
  echo "console-auth-delegator"
  echo "cluster-network-operator"
  echo "control-plane-machine-set-operator"
  echo "crc-csi-hostpathplugin-health-monitor-controller-cluster-role"
  echo "crc-csi-hostpathplugin-provisioner-cluster-role"
  echo "crc-hostpath-csi-provisioner-role"
  echo "csi-snapshot-controller-operator-clusterrole"
  echo "custom-account-openshift-machine-config-operator"
  echo "machine-api-controllers-baremetal"
  echo "machine-os-builder"
  echo "machine-os-builder-anyuid"
  echo "network-node-identity"
  echo "basic-users"
  echo "cloud-credential-operator-rolebinding"
  echo "cluster-admin"
  echo "cluster-admins"
  echo "cluster-autoscaler"
  echo "cluster-autoscaler-operator"
  echo "cluster-baremetal-operator"
  echo "cluster-monitoring-operator"
  echo "cluster-node-tuning-operator"
  echo "cluster-node-tuning:tuned"
  echo "cluster-readers"
  echo "cluster-samples-operator"
  echo "cluster-samples-operator-proxy-reader"
  echo "cluster-status-binding"
  echo "cluster-storage-operator-role"
  echo "cluster-version-operator"
  echo "console"
  echo "console-extensions-reader"
  echo "console-operator"
  echo "console-operator-auth-delegator"
  echo "csi-snapshot-controller-operator-role"
  echo "default-account-cluster-image-registry-operator"
  echo "default-account-cluster-network-operator"
  echo "default-account-openshift-machine-config-operator"
  echo "dns-monitoring"
  echo "helm-chartrepos-view"
  echo "insights-operator"
  echo "insights-operator-auth"
  echo "insights-operator-gather"
  echo "insights-operator-gather-reader"
  echo "kube-apiserver"
  echo "kubeadmin"
  echo "machine-api-controllers"
  echo "machine-api-operator"
  echo "machine-config-controller"
  echo "machine-config-daemon"
  echo "machine-config-server"
  echo "marketplace-operator"
  echo "metrics-daemon-sa-rolebinding"
  echo "multus"
  echo "multus-admission-controller-webhook"
  echo "multus-whereabouts"
  echo "network-diagnostics"
  echo "olm-operator-binding-openshift-operator-lifecycle-manager"
  echo "openshift-.*"
  echo "packageserver-service-system:auth-delegator"
  echo "prometheus-k8s-scheduler-resources"
  echo "registry-monitoring"
  echo "registry-registry-role"
  echo "router-monitoring"
  echo "self-access-reviewers"
  echo "self-provisioners"
  echo "system-bootstrap-node-bootstrapper"
  echo "system-bootstrap-node-renewal"
  echo "system:.*"
  echo "cloud-node-manager"
  echo "machine-api-operator-ext-remediation"
  echo "cloud-controller-manager"
  echo "cluster-samples-operator-imageconfig-reader"
  echo "local-path-provisioner-bind"
  echo "multus-.*"
  echo "storage-version-migration-migrator"
}

create_expandable_storage_class_k8s(){
  cat << EOF | kubectl apply -f - > /dev/null
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: $CRC_EXPANDABLE_STORAGE_CLASSNAME
provisioner: rancher.io/local-path
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
EOF

  printf '%s' "$CRC_EXPANDABLE_STORAGE_CLASSNAME"
}

